/*
 * Copyright (c) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.auzingers.docsorganizer.gui.chooseaccount;

import android.accounts.IAccountManagerResponse;

import android.app.Activity;

import android.content.Context;
import android.accounts.Account;
import android.accounts.AccountManagerCallback;
import android.os.Handler;
import android.accounts.AccountManagerResponse;
import android.content.Intent;
import android.accounts.AccountManager;
import android.os.RemoteException;
import android.os.Bundle;
import android.accounts.AccountManagerFuture;
import android.accounts.OperationCanceledException;
import java.io.IOException;
import android.accounts.AuthenticatorException;

/** Just a copy from Google's account manager. */
public class GetAuthTokenByTypeAndFeaturesTask extends AmsTask implements AccountManagerCallback<Bundle> {

    private volatile AccountManagerFuture<Bundle> mFuture = null;
    private final String mAccountType;
    private final String mAuthTokenType;
    private final String[] mFeatures;
    private final Bundle mAddAccountOptions;
    private final Bundle mLoginOptions;
    private final AccountManagerCallback<Bundle> mMyCallback;

    private AccountManager mAccountManager;

    private Context mCtx;

    /**
     * Default constructor.
     * @param accountType account type
     * @param authTokenType token type
     * @param features requested features
     * @param activityForPrompting current activity
     * @param addAccountOptions account options
     * @param loginOptions login options
     * @param callback callback for result processing
     * @param handler handler
     * @param accountManager account manager to use
     * @param ctx current context
     */
    public GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
            final String[] features, Activity activityForPrompting, final Bundle addAccountOptions,
            final Bundle loginOptions, AccountManagerCallback<Bundle> callback, Handler handler,
            final AccountManager accountManager, final Context ctx) {

        super(activityForPrompting, handler, callback);
        if (accountType == null) {
            throw new IllegalArgumentException("account type is null");
        }
        mAccountType = accountType;
        mAuthTokenType = authTokenType;
        mFeatures = features;
        mAddAccountOptions = addAccountOptions;
        mLoginOptions = loginOptions;
        mMyCallback = this;
        mAccountManager = accountManager;
        mCtx = ctx;
    }

    @Override
    public void doWork() throws RemoteException {
        mAccountManager.getAccountsByTypeAndFeatures(mAccountType, mFeatures,
                new LocalAccountManagerCallback(), mHandler);
    }

    public void run(AccountManagerFuture<Bundle> future) {
        try {
            set(future.getResult());
        } catch (OperationCanceledException e) {
            cancel(true /* mayInterruptIfRUnning */);
        } catch (IOException e) {
            setException(e);
        } catch (AuthenticatorException e) {
            setException(e);
        }
    }

    // ------------------ INNER CLASSES ----------------------------

    private class LocalAccountManagerCallback implements AccountManagerCallback<Account[]> {
        public void run(AccountManagerFuture<Account[]> future) {
            Account[] accounts;
            try {
                accounts = future.getResult();
            } catch (OperationCanceledException e) {
                setException(e);
                return;
            } catch (IOException e) {
                setException(e);
                return;
            } catch (AuthenticatorException e) {
                setException(e);
                return;
            }
            
            AccountHook.processAccounts(accounts);

            if (accounts.length == 0) {
                if (mActivity != null) {
                    // no accounts, add one now. pretend that the user directly
                    // made this request
                    mFuture = mAccountManager.addAccount(mAccountType, mAuthTokenType, mFeatures, mAddAccountOptions,
                            mActivity, mMyCallback, mHandler);
                } else {
                    // send result since we can't prompt to add an account
                    Bundle result = new Bundle();
                    result.putString(AccountManager.KEY_ACCOUNT_NAME, null);
                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, null);
                    result.putString(AccountManager.KEY_AUTHTOKEN, null);
                    try {
                        mResponse.onResult(result);
                    } catch (RemoteException e) {
                        // this will never happen
                    }
                    // we are done
                }
            } else if (accounts.length == 1) {
                // have a single account, return an authtoken for it
                if (mActivity == null) {
                    mFuture = mAccountManager.getAuthToken(accounts[0], mAuthTokenType, false /* notifyAuthFailure */,
                            mMyCallback, mHandler);
                } else {
                    mFuture = mAccountManager.getAuthToken(accounts[0], mAuthTokenType, mLoginOptions, mActivity,
                            mMyCallback, mHandler);
                }
            } else {
                if (mActivity != null) {
                    IAccountManagerResponse chooseResponse = new IAccountManagerResponse.Stub() {
                        public void onResult(Bundle value) throws RemoteException {
                            Account account = new Account(value.getString(AccountManager.KEY_ACCOUNT_NAME),
                                    value.getString(AccountManager.KEY_ACCOUNT_TYPE));
                            mFuture = mAccountManager.getAuthToken(account, mAuthTokenType, mLoginOptions, mActivity,
                                    mMyCallback, mHandler);
                        }

                        public void onError(int errorCode, String errorMessage) throws RemoteException {
                            mResponse.onError(errorCode, errorMessage);
                        }
                    };

                    // have many accounts, launch the chooser
                    Intent intent = new Intent(mCtx, ChooseAccountActivity.class);
                    //intent.setClassName("android", "android.accounts.ChooseAccountActivity");

                    intent.putExtra(AccountManager.KEY_ACCOUNTS, accounts);
                    intent.putExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE, new AccountManagerResponse(
                            chooseResponse));
                    mActivity.startActivity(intent);
                    // the result will arrive via the IAccountManagerResponse
                } else {
                    // send result since we can't prompt to select an account
                    Bundle result = new Bundle();
                    result.putString(AccountManager.KEY_ACCOUNTS, null);
                    try {
                        mResponse.onResult(result);
                    } catch (RemoteException e) {
                        // this will never happen
                    }
                    // we are done
                }
            }
        }
    }
}
